//------------------------------------------------//
// Decompressor and Recompressor for ARETHA 2     //
//                                                //
// Coded By: John C. ( Bongo` )                   //
//           April 1st, 2005                      //
//                                                //
// Compiled with [ PROJECT ] Borland C++ Builder 3//
// Compiled with [ SOURCE ONLY ] Turbo C++ 3.0    //
//------------------------------------------------//

#include <dir.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#define tc(x, y) textcolor(x); textbackground(y)

#ifndef LONG
 typedef unsigned char  BYTE;
 typedef unsigned short WORD;
 typedef unsigned long  LONG;
#endif

#define WRAM(x) *(short *)&ram[x]
#define LRAM(x) *(long  *)&ram[x]

	struct REGRS
	{ //
	 short A,
				 X,
				 Y;
		BYTE a,
				 x,
				 y;
		LONG bpos,
				 cpos;
	}reg;

	BYTE ram[0x20000];

  LONG size;
  LONG lof,
       dBegPosi,
       dMaxPosi;

  int  cBack,
       cMost,
       cTestCount,
       cMostPos;

  WORD c,
       index,
       rHeader;
  WORD flag;

  BYTE *ControlByte;

  FILE *rf,
       *df,
       *tf;

  char rom[260];
  char dat[260];
  char txt[260];

  int  decompress( long );
  int  recompress( long , long );
	int  BetterCompTest( long );
  int  LoadScriptFile( char *scFile, LONG index );

	LONG FileLen( FILE *f );
  LONG SNES2ROM( LONG value );

	void _Decomp_TEXT( int argc, char *argv[ ] );
	void _Recomp_TEXT( int argc, char *argv[ ] );

  void _HelpUser( );

//-------------------------//
//                         //
// Main data routine start //
//                         //
//-------------------------//
int  main( int argc, char *argv[ ] )
{ //
	tc(15, 0);
	clrscr( );

	printf( "Aretha 2 Compression Tool version 0.95\n"
					"For help, press 'H'\n\n"
					"  [1] - Decompress ALL TEXT files.\n"
					"  [2] - Recompress TEXT file(s)\n"
					"  [3] - Exit program\n\n\n"

					"DRAAGED and DROPPED File:\n"
					"\"%s\"\n\n\n",
					argv[1]
				);

	c = 0;
	while ( !c )
	{
		c = getch( );
		switch( c )
		{
			case '1': _Decomp_TEXT( argc, argv ); c = 1; break;
//      case '2': _Recomp_TEXT( argc, argv ); c = 1; break;
			case 'h':
			case 'H': _HelpUser( ); c = 0;
								tc(15, 0); clrscr( );
								printf( "Aretha 2 Compression Tool version 0.95\n"
												"For help, press 'H'\n\n"
												"  [1] - Decompress ALL TEXT files.\n"
												"  [2] - Recompress TEXT FILE(S)\n"
												"  [3] - Exit program\n\n\n"

												"DRAAGED and DROPPED File:\n"
												"\"%s\"\n\n\n",
												argv[1]
											);
								break;
			case '4':
			case 27 : c = 1; break;
			default : c = 0; break;
		}
	}

  return 0;
}// END MAIN



//-----------------------//
// All the files         //
//-----------------------//
void _Decomp_TEXT( int argc, char *argv[ ] )
{
	// we have a file
	if ( argc > 1 )
	{
		strcpy(rom, argv[1] );
		strcpy(dat, argv[1] );
		strcpy(txt, argv[1] );
		//
		rf = fopen(rom,"rb");
		// test the files to bew opened
		if ( !rf ){
			printf("Fatal Error: Could not open a file.");
			return;
		}

		fseek(rf, 0, SEEK_END );
		size = ftell( rf );
		fseek(rf, 0, SEEK_SET );
		// Header??
		rHeader = (size & 0x0200);

		c = strlen(dat);
		while( dat[ c ] != '\\' && dat[ c ] != '/' ) c--;
		c++;
		// text file
		sprintf( &txt[c], "DeComp\\" );
		mkdir( txt );

		sprintf( &txt[c], "DeComp\\AR_Data.txt" );
		tf = fopen( txt, "wt" );
		if ( !tf ){
			printf("Error: Could not open DATA file.");
			return;
		}

		fprintf( tf, "// Filename, STRUCT Offset, BLOCK Index, DATA Start, DATA End\n\n" );
		index = 0;
		while( index < 0x10 )
		{ //
			sprintf( &dat[c], "DeComp\\AR_%04d.dat", index );
			fseek( rf, SNES2ROM( 0x228000 + (index << 1) ), SEEK_SET );
			fread( ram, 1, 2, rf );
			WRAM(0x02) = 0x0022;
			//
			if ( decompress( SNES2ROM( (LRAM(0x00) & 0x00FFFFFF) ) ) != 0 )
				break;
			index++;
		}
		//
		fclose( tf );
	}
}



//-----------------------//
// Recompress the file.  //
//-----------------------//
void _Recomp_TEXT( int argc, char *argv[ ] )
{
	LONG index = 0;

	if ( argc > 1 )
	{
		while( LoadScriptFile( argv[1], index ) != -1 )
		{
	//    if ( LoadScriptFile( argv[1] ) == -1 ) return;
			//
			rf = fopen( rom, "r+b" );
			if ( !rf ){
				printf("Fatal Error: Could not open ROM file.");
				return;
			}

			//
			fseek(rf, 0, SEEK_END );
			size = ftell( rf );
			fseek(rf, 0, SEEK_SET );
			// Header??
			rHeader = (size & 0x0200);

			recompress( dBegPosi, dMaxPosi );
			fclose( rf );

			index++;
		}// WEND

		printf("\n\nPress and key to continue");
		getch( );
	}
}


/////////////////////////
//                     //
/////////////////////////
int  decompress( long offset )
{
	int erCount;

	// Init some data
	memset( &reg, 0, sizeof(reg) );
	memset( ram, 0, 0x20000 );

	reg.X     = 0x0000;
	reg.bpos  = 0x10000;
	erCount   = 0;

	df = fopen( dat, "wb" );
	// test the files to be opened
	if ( !df ){
		printf("Fatal Error: Could not open DESTINATION file.");
		return -1;
	}

	// Record data info, SCRIPT TAG, ROM name, DAT name, COMP Type, ROM Table position, DATA Start position
	fprintf( tf, ".COMP %s %s  %08X  %08X  ", rom, dat, SNES2ROM( 0x228000 + (index << 1) ), offset );

	//-------------------------//
	// Go to Data position     //
	//-------------------------//
	fseek(rf, offset, SEEK_SET);
	// Get the data size
	fread( &ram[ 0x0C ], 1, 2, rf );
	WRAM(0x0C) &= 0x7FFF;
	WRAM(0x0C) += 1;
	// Temp buffer offset
	WRAM(0x08)  = 0;

	//-------------------------//
	// Routine main loop       //
	//-------------------------//
	while ( WRAM(0x0C) )
	{ // Load up the test byte
		ram[0x10] = fgetc( rf );
		ram[0x11] = 8;
		//
		// Loop thru 8-Bits
		while( ram[0x11]-- )
		{	// If bit SET
			if ( ram[0x10] & 1 )
			{ // Static data
				ram[ reg.bpos ] =
				ram[ 0xF000 + reg.X ] = fgetc( rf );
				reg.bpos++;
				reg.X++;
				reg.X &= 0x0FFF;
				WRAM(0x0C) -= 1;

				erCount++;
				// End of compression test
				if ( !WRAM(0x0C) ) break;
			}
			else
			{ // Compressed data
				fread( &ram[0x0A], 1, 2, rf );
				ram[0x0E] = ((ram[0x0B] >> 3) & 0x1E) + 0x03;
				WRAM(0x0A) &= 0x0FFF;
				// Loop and copy data
				while( ram[0x0E]-- ){
					ram[ reg.bpos ] =
					ram[ 0xF000 + reg.X ] =
					ram[ 0xF000 + WRAM(0x0A) ];
					//
					reg.bpos++;
					reg.X++;
					reg.X &= 0x0FFF;
					WRAM(0x0A) += 1;
					WRAM(0x0A) &= 0x0FFF;
					//
					WRAM(0x0C) -= 1;

  				erCount++;					
					// End of compression test
					if ( !WRAM(0x0C) ) break;
				}
			}// END IF

			ram[0x0F] = ram[0x10] & 1;
			ram[0x10] >>= 1;
			// End of compression test
			if ( !WRAM(0x0C) ) break;
		} // WEND

	}// WEND

	// Store the data
	fwrite( &ram[ 0x10000 ], 1, (reg.bpos - 0x10000), df );

	//---------------------------//
	// Record the ending address //
	//---------------------------//
	fprintf( tf, "%08X\n", ftell( rf ) );

	printf("\n%s dumped successful!", dat);
	fclose( df );

	return 0;
}


/////////////////////////////////////
//
/////////////////////////////////////
int recompress( long dsBegPos, long dsMaxPos )
{
	int  b_1;
/*
	// Init some data
	memset(&reg,0, sizeof(reg));
	memset(buf, 0, 0xFFFF);
	memset(ram, 0, 0xFFFF);
	memset(cmp, 0, 0xFFFF);

	// check RAW data file
	df = fopen(dat, "rb");
	if ( !df ){
	printf("DATA file not found...\n%s", dat );
		return -1;
	} // END IF

	lof = FileLen( df );

	// store data to be compressed in rbuf
	fread(buf, 1, lof, df);
	fclose( df );

	/////////////////////////////////////////
	// Start of Main Recompression Routine //
	// loop through all of the data        //
	/////////////////////////////////////////
	reg.bpos    = 1;
	reg.cpos    = 0;
	reg.x       = 0x1FEE;
	ControlByte = &cmp[0x0000];
	while( reg.cpos < lof )
	{
	ram[0xC7] = 0;
	// loop through 8 bits
	while( ram[0xC7] < 8 )
		{ //
			cBack = 0x1FFF;
			//------------------------------//
			// Search for compressable data //
			// Do this going backwards...   //
			//------------------------------//
			cMost = 0;
			while( cBack >= 0x1000 )
			{ //
				if ( buf[ reg.cpos ] == ram[ cBack ] ) {
					cTestCount=0;
					// loop while new data matches old data
					while( buf[ reg.cpos + cTestCount ] == ram[ ((cBack + cTestCount) % 0x2000) | 0x1000 ] && ((reg.cpos + cTestCount) < lof) && cTestCount < 18 )
						cTestCount++;

					// If more data found, change most
					if ( (cTestCount > cMost) ) {
						cMostPos = cBack;
						cMost    = cTestCount;
					}// END IF

					// break if the maxium amount of data is reached
					if ( cMost > 17 ){
						cMost = 18;
						break;
					}
				} // END IF

				cBack--;
				// Test for breaking
				if ( cBack == 0x1000 ) break;
				if ( (cBack - 0x12) < 0x1000 ) break;
				//
        if ( cBack == reg.x ) cBack -= 0x12;
        cBack = (cBack % 0x2000) | 0x1000;
      }// WEND - cBack < reg.cpos

      //---------------------------//
      // Do a test to make sure    //
      // the next two bytes aren't //
      // better to compress.       //
      //---------------------------//
      if ( cMost > 2 )
      {
        b_1 = BetterCompTest( reg.cpos + 1 ) - 1;
        if ( b_1 > cMost ){
          cMost = 1; b_1 = 0;
					goto _NextMatch;
				}
				//
			}

			//-------------------------------------------//
			// Test for Regular or Compress data storage //
			//-------------------------------------------//
			_NextMatch:
			if ( cMost > 2 )
			{ // Data was a compressed string
				WRAM(0xC0)   = (cMost - 3);
				WRAM(0xC0)  |= ((cMostPos & 0x0F00) >> 4);
				WRAM(0xC0) <<= 8;
				WRAM(0xC0)  += ( cMostPos & 0x00FF );
				//
				cmp[ reg.bpos ] = (WRAM(0xC0) & 0x00FF);
				reg.bpos++;
				cmp[ reg.bpos ] = (WRAM(0xC0) >> 8);
				reg.bpos++;
				//
				ram[ ram[0xC7] ] ='0';
				//
				while( cMost > 0 ){
					ram[ reg.x ] = buf[ reg.cpos ];
					// Add to the buffer
					reg.x++;
					reg.cpos++;
					if ( reg.x > 0x1FFF ) reg.x = 0x1000;
					cMost--;
				}
			}
			else
			{ // Store the regular data
				cmp[ reg.bpos ] = buf[ reg.cpos ];
				ram[ reg.x ]    = buf[ reg.cpos ];
				reg.x++;
				reg.cpos++;
				reg.bpos++;
				//
				if ( reg.x > 0x1FFF ) reg.x = 0x1000;
				//
				ram[ ram[0xC7] ]         = '1';
			}// END IF

			ram[0xC7]++;
	} // WEND - ram[0xC7] < 8

		// Null out the string of BITS
		ram[0x08] = 0;

	// select which function to use by the routine type's flag
		strrev( ram );
		ControlByte[ 0 ] = strtoul( ram, NULL, 2 );
		ControlByte = &cmp[ reg.bpos ];
		reg.bpos++;
	}// WEND

	//-----------------------------------------//
	// Store the size of the data to be stored //
	//-----------------------------------------//
	if ( (dsBegPos + reg.bpos) < dsMaxPos )
	{ // Store what data can be store to ROM
		fseek( rf, dsBegPos, SEEK_SET );
		fwrite( cmp, 1, reg.bpos, rf );
	printf("%s compressed successfully\n\n", dat );
	}
	else
	printf("%s didn't not compress completely.\n[ BYTES OVER: %d ]\n\n", dat, (dsBegPos + reg.bpos) - dsMaxPos );

//  fwrite( cmp, 1, reg.bpos, rf );
*/
	return 0;
}// End Of SUB



//////////////////////////////////
//                              //
//////////////////////////////////
LONG FileLen( FILE *f )
{
	LONG LOF,
			 POS;

  POS = ftell( f );
	fseek(f, 0, SEEK_END );
  LOF = ftell( f );
  fseek(f, POS, SEEK_SET );

  return LOF;
}


//////////////////////////////////
//                              //
//////////////////////////////////
LONG SNES2ROM( LONG value )
{
  LONG valu;

	valu = (value & 0x7FFFFF );
	valu = (( valu & 0xFF0000 ) >> 1 ) + ( (valu & 0xFFFF) - 0x8000 ) + rHeader;

  return valu;
}



//-----------------------------------//
// This check is to make sure that   //
// the NEXT byte or so aren't better //
// for compression!!!!               //
//-----------------------------------//
int  BetterCompTest( long tbPosi )
{
  int  tbBack    = cBack,
       tbMost    = cMost,
       tbMostPos = cMostPos,
       tbFlag = 0;

/*  cBack = 0x1FFF;
  //------------------------------//
  // Search for compressable data //
  // Do this going backwards...   //
  //------------------------------//
  cMost = 0;
  while( cBack >= 0x1000 )
  { //
    if ( buf[ tbPosi ] == ram[ cBack ] ) {
      cTestCount=0;
      // loop while new data matches old data
      while( buf[ tbPosi + cTestCount ] == ram[ ((cBack + cTestCount) % 0x2000) | 0x1000 ] && ((tbPosi + cTestCount) < lof) && cTestCount < 18 )
        cTestCount++;

      // If more data found, change most
      if ( (cTestCount > cMost) ) {
        cMostPos = cBack;
        cMost    = cTestCount;
      }// END IF

      // break if the maxium amount of data is reached
      if ( cMost > 17 ){
        cMost = 18;
        break;
      }
    } // END IF

    cBack--;
    // Test for breaking
    if ( cBack == 0x1000 ) break;
    if ( (cBack - 0x12) < 0x1000 ) break;
    //
    if ( cBack == reg.x ) cBack -= 0x12;
    cBack = (cBack % 0x2000) | 0x1000;
	}// WEND - cBack < reg.cpos

	tbFlag   = cMost;
	//
	cBack    = tbBack;
	cMost    = tbMost;
	cMostPos = tbMostPos;
*/
  return tbFlag;
}


//------------------------------//
// Load a comp data file to use //
// for any file recompression   //
//------------------------------//
int LoadScriptFile( char *scFile, LONG index )
{
  LONG OFFSET,
      crLINE = 0;
  FILE *sf;
  char buf[512];

  sf = fopen( scFile, "rt" );
  if ( !sf ){
    printf("Fatal Error: Could not open SCRIPT file for recompression." );
    return -1;
  }

  //
  while( fgets(buf, 256, sf ) )
  {
    for ( int x = 0; x < strlen(buf); x++ )
      buf[x] = toupper( buf[x]);
    //
    if ( strstr(buf, ".COMP") ){
      // tag, rom, dat, table pos, data beg_pos, data end_pos
      sscanf( buf, "%s %s %s %X %X %X", &txt, &rom, &dat, &OFFSET, &dBegPosi, &dMaxPosi );
      // Make sure we have the correct index
      if ( crLINE != index ){ crLINE++; continue; }
      fclose( sf );
      return 0;
    }
  }

  fclose( sf );
  return -1;
}


//------------------------------//
// Give the user some damn help //
//------------------------------//
void _HelpUser( )
{
  int  p;
  char buf[256];

  clrscr( );

	tc(14, 0);
	cprintf(
		" Decompress ALL data:"
	);

	printf("\n"
		"   This decompresses all the compressed blocks\n"
		"   available within the ROM's compression TABLE.\n"
		" { ROM-FILE MUST BE DRAGGED and DROPPED INTO THE PROGRAM! }\n\n"
	);

	tc(14, 0);
	cprintf(
		" Decompress PROJECT files only:"
	);

	printf("\n"
		"    Decompress only the PROJECT files within the ROM.\n"
		"    These PROJECT files are all the files that need to\n"
		"    be edited in order to translate the project.\n\n"
		" { ROM-FILE MUST BE DRAGGED and DROPPED INTO THE PROGRAM! }\n\n"
	);

	tc(14, 0);
	cprintf(
		" Recompress FILE(S):"
	);

	printf("\n"
		"    Recompress any available files that are listed\n"
		"    in the DK_Data.txt file. This file is a crucial\n"
		"    part of the recompression process. It's the ONLY\n"
		"    way to recompress ANYTHING!!!\n\n"
		" { DQ_DATA.TXT MUST BE DRAGGED and DROPPED INTO THE PROGRAM! }\n\n"

		"\n\nPress ENTER to return..."
	);

	p = 0;
	while( p != 13 )
		p = getch( );
}



